
package com.joyintech.modules.workflow.service.impl;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;
import com.joyintech.base.PageInfo;
import com.joyintech.modules.workflow.service.WorkFlowService;
import com.joyintech.modules.workflow.service.dto.ModelQueryCriteria;
import com.joyintech.utils.PageUtil;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.*;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipInputStream;

@Service
public class WorkFlowServiceImpl implements WorkFlowService {
    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;


    @Override
    public PageInfo<Map<String, Object>> listProcessDefinitions(ModelQueryCriteria queryCriteria, Pageable pageable) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery()
                .latestVersion()
                .orderByProcessDefinitionKey().asc();
        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.listPage((int)pageable.getOffset(),pageable.getPageSize());
        List<Map<String, Object>> list = Lists.newArrayList();
        for (ProcessDefinition processDefinition : processDefinitionList) {
            Map<String, Object> map = new HashMap<String, Object>();
            String deploymentId = processDefinition.getDeploymentId();
            map.put("id", processDefinition.getId());
            map.put("name",processDefinition.getName());
            map.put("key", processDefinition.getKey());
            map.put("deploymentId", processDefinition.getDeploymentId());
            map.put("dgrmResourceName", processDefinition.getDiagramResourceName());
            map.put("resourceName", processDefinition.getResourceName());
            map.put("state", processDefinition.isSuspended());
            map.put("version",processDefinition.getVersion());
            String deployName = processDefinition.getName();
            if(StringUtils.isBlank(deployName)){
                Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
                deployName = deployment == null ? null : deployment.getName();
            }
            Model model = repositoryService.createModelQuery().deploymentId(deploymentId).latestVersion().singleResult();
            if(model!=null){
                map.put("modelId",model.getId());
            }
            map.put("name", deployName);
            list.add(map);
        }
        return PageUtil.toPage(list,processDefinitionQuery.count());
    }

    @Override
    public void uploadProcessDefinitions( MultipartFile file,boolean isSyncConvert) throws Exception {
        String fileName = file.getOriginalFilename();
        InputStream fileInputStream = file.getInputStream();
        Deployment deployment = null;
        String extension = FilenameUtils.getExtension(fileName);
        if ("zip".equals(extension) || "bar".equals(extension)) {
            ZipInputStream zip = new ZipInputStream(fileInputStream);
            deployment = repositoryService.createDeployment().addZipInputStream(zip).deploy();
        } else if ("png".equals(extension)) {
            deployment = repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
        } else if (fileName.indexOf("bpmn20.xml") != -1) {
            deployment = repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
        } else if ("bpmn".equals(extension)) { // bpmn扩展名特殊处理，转换为bpmn20.xml
            String tempName = FilenameUtils.getBaseName(fileName) + ".bpmn20.xml";
            deployment = repositoryService.createDeployment().addInputStream(tempName, fileInputStream).deploy();
        } else {
            throw new RuntimeException("文件不支持");
        }
        ProcessDefinition processDefinition =    repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
        if(processDefinition==null){
            throw new RuntimeException("没有流程定义文件");
        }
        if(isSyncConvert)  convertToModel(processDefinition);
    }

    @Override
    public void deleteProcessDefinitions(Set<String> ids,boolean cascade) {
        ids.stream().forEach(deploymentId->repositoryService.deleteDeployment(deploymentId, cascade));
    }

    @Override
    public void updateProcessDefinitions(String procDefId, String state)  {
        if ("active".equals(state)) {
            try {
                repositoryService.activateProcessDefinitionById(procDefId, true, null);
            } catch (Exception e) {
                throw new RuntimeException();
            }
        } else if ("suspend".equals(state)) {
            try {
                repositoryService.suspendProcessDefinitionById(procDefId, true, null);
            } catch (Exception e) {
                throw new RuntimeException();
            }
        }
    }

    @Override
    public void convertProcessDefinitions(String deploymentId) throws Exception {
        ProcessDefinition processDefinition =    repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        if(processDefinition==null){
            throw new RuntimeException();
        }
        convertToModel(processDefinition);
    }

    @Override
    public void showProcessDefinitionsDiagrams(HttpServletResponse response, String deploymentId) throws Exception {

       /* ProcessDefinition processDefinition =    repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();


        InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                processDefinition.getResourceName());


        IOUtils.copy(resourceAsStream,response.getOutputStream());*/


        /*  转为模型后才能调用*/
        ProcessDefinition processDefinition =    repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
        BpmnModel model = repositoryService.getBpmnModel(processDefinition.getId());
       // InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());
        ProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();
        InputStream inputStream = processDiagramGenerator.generateDiagram(model,  "宋体","宋体","宋体");
        IOUtils.copy(inputStream, response.getOutputStream());

    }



    @Override
    public PageInfo<Model> listModel(ModelQueryCriteria queryCriteria, Pageable pageable) throws Exception {
        ModelQuery modelQuery = repositoryService.createModelQuery().latestVersion().orderByLastUpdateTime().desc();
        List<Model> list = modelQuery.listPage((int) pageable.getOffset(), pageable.getPageSize());
        for (Model m: list) {
           // m.setName(String.valueOf(JSONUtils.toMap(m.getMetaInfo()).get("name")));
        }
        return PageUtil.toPage(list,modelQuery.count());
    }
    @Override
    public void downloadModel(String id) {

    }

    @Override
    public void deleteModel(Set<String> ids) {

    }

    @Override
    public void deployModel(String id) {

        try {
            org.activiti.engine.repository.Model modelData = repositoryService.getModel(id);
            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
            JsonNode editor = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editor);

            BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
            byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);

            String processName = modelData.getName();
            if (!StringUtils.endsWith(processName, ".bpmn20.xml")) {
                processName += ".bpmn20.xml";
            }
            ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
            Deployment deployment = repositoryService.createDeployment().name(
                    modelData.getName()).addInputStream(processName, in).deploy();
            // 设置流程分类
            List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().deploymentId(
                    deployment.getId()).list();
            for (ProcessDefinition processDefinition: list) {
                repositoryService.setProcessDefinitionCategory(
                        processDefinition.getId(), modelData.getCategory());

            }
            if (list.size()==0) {
                throw new RuntimeException();
            }
        } catch (Exception e) {
            throw new RuntimeException("设计模型图不正确，检查模型正确性，模型ID="+id, e);
        }

    }


    //流程定义转模型
    private void convertToModel( ProcessDefinition processDefinition) throws Exception {
        org.activiti.engine.repository.Model modelData = null;
        InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                processDefinition.getResourceName());
        XMLInputFactory xif = XMLInputFactory.newInstance();
        InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
        XMLStreamReader xtr = xif.createXMLStreamReader(in);
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);

        BpmnJsonConverter converter = new BpmnJsonConverter();
        ObjectNode modelNode = converter.convertToJson(bpmnModel);
        modelData = repositoryService.newModel();
        modelData.setKey(processDefinition.getKey());
        modelData.setName(processDefinition.getResourceName());
        modelData.setCategory(processDefinition.getCategory());// .getDeploymentId());
        modelData.setDeploymentId(processDefinition.getDeploymentId());
        modelData.setVersion(Integer.parseInt(
                String.valueOf(repositoryService.createModelQuery().modelKey(modelData.getKey()).count() + 1)));

        ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
        modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, modelData.getVersion());
        modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
        modelData.setMetaInfo(modelObjectNode.toString());

        repositoryService.saveModel(modelData);
        repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
    }

    @Override
    public void deleteProcessInstance(String procInsId, String reason) {
        runtimeService.deleteProcessInstance(procInsId, reason);
    }
}
